A compiler is routinely used to translate a high level computer program and produce object files containing machine code. A linker is then routinely used to bind procedures in the object files to form an executable program that is ready for execution on a target machine. A group of compiled object files may be collected to form a library, and a table of contents or directory identifies the procedures in the library object files and how to extract the procedures from the library object files. If necessary, the linker can extract the necessary object files from a library and use those object files to create the executable program.
Ideally, a compiler generates machine code that is as efficient as possible, thereby helping to ensure that an executable program can be executed as fast as possible. Techniques for increasing the efficiency of machine code generated by compilers are typically referred to as “compiler optimization.” One conventional approach to compiler optimization is called “interprocedural optimization.” Using this approach, a compiler detects characteristics of a target procedure and then uses those characteristics to optimize procedures that invoke or that are invoked by the target procedure. However, conventional interprocedural optimization techniques typically cannot work or work poorly when object files in a library are used during compilation. In particular, the conventional approaches are unable to actually optimize procedures in library object files. Moreover, some programs or systems actually require or mandate the use of libraries. In addition, the steps taken to create object files in a library are often different from the steps taken to create the object files to which interprocedural optimization is being applied. These problems typically cause a reduction in the efficiency of machine code generated by the compiler. This, in turn, typically reduces the execution speed of the corresponding executable program.